package com.alibaba.nacos.common.task.engine;

import com.alibaba.nacos.common.task.AbstractDelayTask;
import com.alibaba.nacos.common.task.NacosTaskProcessor;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
public class NacosDelayTaskExecuteEngine
        extends AbstractNacosTaskExecuteEngine<AbstractDelayTask> {

    protected final ConcurrentHashMap<Object, AbstractDelayTask> tasks = new ConcurrentHashMap<>(32);
    private final ScheduledExecutorService processingExecutor = Executors.newSingleThreadScheduledExecutor(r -> Thread.ofVirtual().unstarted(r));

    {
        processingExecutor.scheduleWithFixedDelay(() -> {
            try {
                processTasks();
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }, 100, 100, TimeUnit.MILLISECONDS);
    }

    private void processTasks() {
        for (Iterator<Map.Entry<Object, AbstractDelayTask>> iterator = tasks.entrySet().iterator(); iterator.hasNext(); ) {
            Map.Entry<Object, AbstractDelayTask> entry = iterator.next();
            if (!entry.getValue().shouldProcess()) {
                continue;
            }

            iterator.remove();

            NacosTaskProcessor processor = getProcessor(entry.getKey());
            boolean success = processor.process(entry.getValue());
            if (!success) {
                log.warn("process task failed, key={}, task={}", entry.getKey(), entry.getValue());
            }
        }
    }

    public static void main(String[] args) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < 10; i++) {
            map.put(i, i);
        }

        for (Iterator<Map.Entry<Integer, Integer>> iterator = map.entrySet().iterator(); iterator.hasNext(); ) {
            Map.Entry<Integer, Integer> entry = iterator.next();
            iterator.remove();
            System.out.println(entry.getValue());
        }

    }

    @Override
    public void addTask(Object key,
                        AbstractDelayTask task) {
        tasks.compute(key, (object, oldTask) -> {
            Optional.ofNullable(oldTask).ifPresent(task::merge);
            return task;
        });
    }


}
